import numpy as np
import pandas as pd
import time,os
from tkinter import *
from threading import Timer
from tkinter import messagebox
import tkinter as tk
np.random.seed(2)
N_STATES = 25 # 二维世界的序号
ACTIONS = ['left','right','up','down'] # 探索者可选择的动作
EPSIONS = 0.0 # greedy police贪婪度
ALPHA = 0.1 # 学习效率
LAMBDA = 0.9 # 未来奖励的衰减值
MAX_EPISODES = 13 # 最大的回合数
FRESH_TIME = 0.001 # 走一步所花费的时间,移动时间间隔
class basedesk():#底板
    def __init__(self, master):
        self.master = master
        self.master.title("小安子历险记")
        self.master.configure(bg='#E2C07C')
        self.master.geometry("1000x560")
        mainwindow(self.master)
class mainwindow():#主界面
    def __init__(self, master):
        self.master = master
        self.point_num=1
        self.window = tk.Frame(self.master, bg='#E2C07C')
        self.window.place(x=0,y=0,width=1000,height=560)
        self.window.showmap_label = tk.Label(self.window,text="迷宫地图",fg='#000000',bg='#B1EBC2',font=("Helvetic",20,"bold"),relief=RAISED).place(x=10, y=10,width=500, height=30)
        self.map_canvas = tk.Canvas(self.window,bg='#EAF4C0',relief=SUNKEN)
        self.map_canvas.place(x=10,y=50, width=500, height=500)
        for i in range(4):
            self.map_canvas.create_line(0, (i+1)*100, 500, (i+1)*100, width=1, capstyle=BUTT, fill='black')
            self.map_canvas.create_line((i + 1) * 100,0, (i + 1) * 100, 500,  width=1, capstyle=BUTT, fill='black')
        UNIT = 100
        origin = np.array([50, 50])
        hell1_center = origin + np.array([UNIT , 0])
        self.hell1 = self.map_canvas.create_rectangle(
            hell1_center[0] - 45, hell1_center[1] - 45,
            hell1_center[0] + 45, hell1_center[1] + 45, fill='black')
        hell2_center = origin + np.array([UNIT*2 , UNIT])
        self.hell2 = self.map_canvas.create_rectangle(
            hell2_center[0] - 45, hell2_center[1] - 45,
            hell2_center[0] + 45, hell2_center[1] + 45, fill='black')
        hell3_center = origin + np.array([UNIT * 3, UNIT])
        self.hell3 = self.map_canvas.create_rectangle(
            hell3_center[0] - 45, hell3_center[1] - 45,
            hell3_center[0] + 45, hell3_center[1] + 45, fill='black')
        hell4_center = origin + np.array([UNIT, UNIT*2])
        self.hell4 = self.map_canvas.create_rectangle(
            hell4_center[0] - 45, hell4_center[1] - 45,
            hell4_center[0] + 45, hell4_center[1] + 45, fill='black')
        hell5_center = origin + np.array([UNIT * 3, UNIT * 3])
        self.hell5 = self.map_canvas.create_rectangle(
            hell5_center[0] - 45, hell5_center[1] - 45,
            hell5_center[0] + 45, hell5_center[1] + 45, fill='black')
        hell6_center = origin + np.array([0, UNIT * 4])
        self.hell6 = self.map_canvas.create_rectangle(
            hell6_center[0] - 45, hell6_center[1] - 45,
            hell6_center[0] + 45, hell6_center[1] + 45, fill='black')
        oval_center = origin + np.array([UNIT * 2 , 0])
        self.oval = self.map_canvas.create_oval(
            oval_center[0] - 45, oval_center[1] - 45,
            oval_center[0] + 45, oval_center[1] + 45, fill='green')
        self.window.showtimes_listbox = tk.Listbox(self.window,font=("Helvetica",20))
        self.window.showtimes_listbox.place(x=510, y=8, width=480, height=30)
        self.refresh_times()
        self.window2 = tk.Frame(self.window, bg='#FFD966')
        self.window2.place(x=510, y=48, width=480, height=500)
        self.window2.submitted_scrollbar = tk.Scrollbar(self.window2)
        self.window2.submitted_scrollbar.pack(side=RIGHT, fill=Y)
        self.window2.submitted_listbox = tk.Listbox(self.window2,font=("Helvetic",15,"bold"), yscrollcommand=self.window2.submitted_scrollbar.set)
        self.window2.submitted_listbox.place(x=0, y=0, width=463, height=500)
        self.window.calculate_btn=tk.Button(self.window,text="冲鸭！",bg='#1CBF4A',fg='#FFFFFF',font=("Helvetic",40, "bold"),command=self.rl_middle)
        self.window.calculate_btn.place(x=510,y=400,width=480, height=150)
        self.anan=tk.Button(self.map_canvas,text="安",bg='#7CE2C9',fg="#000000",font=("Helvetic",40, "bold"))
        self.anan.place(x=10,y=10,width=80, height=80)


    def check_dead(self,S):
        switch =[1,7,8,11,18,20]
        if S in switch:
            return 'hell'
        else:
            return 'live'
    def get_env_feedback(self,S, A):
        if A=='right':
            if S%5==4:
                S_=S
                R = -10000000
            else:
                S_=S+1
                R = -0.1
                if self.check_dead(S_) == 'hell':
                    R = -100
                    S_ = 'terminal'
                    self.window2.submitted_listbox.insert(END, '掉入了深渊，正在复活...')
                    return S_, R
                elif S_ == 2:
                    R = 100
                    S_ = 'terminal'
                    self.window2.submitted_listbox.insert(END, '逃离成功~~~')
                    return S_, R
        elif A=='left':
            if S%5==0:
                S_=S
                R = -10000000
            else:
                S_=S-1
                R=-0.1
                if self.check_dead(S_) == 'hell':
                    R = -100
                    S_ = 'terminal'
                    self.window2.submitted_listbox.insert(END, '掉入了深渊，正在复活...')
                    return S_, R
                elif S_ == 2:
                    R = 100
                    S_ = 'terminal'
                    self.window2.submitted_listbox.insert(END, '逃离成功~~~')
                    return S_, R
        elif A=='up':
            if int(S/5)==0:
                S_ = S
                R = -10000000
            else:
                S_ = S - 5
                R = -0.1
                if self.check_dead(S_) == 'hell':
                    R = -100
                    S_ = 'terminal'
                    self.window2.submitted_listbox.insert(END, '掉入了深渊，正在复活...')
                    return S_, R
                elif S_ == 2:
                    R = 100
                    S_ = 'terminal'
                    self.window2.submitted_listbox.insert(END, '逃离成功~~~')
                    return S_, R
        else:
            if int(S/5)==4:

                S_ = S
                R = -10000000
            else:
                S_ = S + 5
                R = -0.1
                if self.check_dead(S_) == 'hell':
                    R = -100
                    S_ = 'terminal'
                    self.window2.submitted_listbox.insert(END, '掉入了深渊，正在复活...')
                    return S_, R
                elif S_ == 2:
                    R = 100
                    S_ = 'terminal'
                    self.window2.submitted_listbox.insert(END, '逃离成功~~~')
                    return S_, R
        return S_, R
    def rl_middle(self):
        t = Timer(0.1, self.rl)
        t.start()

    def rl(self):
        self.window2.submitted_listbox.insert(END,'开始探索...')
        self.window.calculate_btn.destroy()
        q_table=self.build_q_table(N_STATES,ACTIONS)
        timeshappened=0
        while True:
            timeshappened+=1
            self.window2.submitted_listbox.insert(END, '第'+str(timeshappened)+'次探索...')
            step_counter=0
            S=0
            is_terminated=False
            self.refresh_anan(0,0)
            while not is_terminated:
                time.sleep(0.1)
                A=self.choose_action(S,q_table)
                S_,R=self.get_env_feedback(S,A)
                if S_ != 'terminal':
                    q_target = R + LAMBDA * q_table.iloc[S_, :].max()  # 更新:真实值减去估计值乘以学习效率

                else:
                    q_target = R  # 实际的状态-行为值(回合结束)
                    is_terminated = True
                step_counter += 1
                q_table.loc[S, A] += ALPHA * (q_target)
                S = S_
                if S_ != 'terminal':
                    self.refresh_anan(S % 5, int(S / 5))

            self.window2.submitted_listbox.insert(END,'本次尝试步数：'+str(step_counter)+'步')
            '''for elements in self.recorder:
                q_table.loc[elements[0], elements[1]] += ALPHA*(q_target/step_counter)'''  # 更新
            self.point_num+=1
            self.refresh_times()


    def build_q_table(self,n_states,actions):
        table = pd.DataFrame(
            np.zeros((n_states, len(actions))),  # q_table initial values全零初始化
            columns=actions,  # actions name
        )
        return table
    def choose_action(self,state, q_table):
        global EPSIONS
        EPSIONS+=0.05
        if EPSIONS>1.0:
            EPSIONS=1.0
        state_actions = q_table.iloc[state, :]
        if np.random.uniform() > EPSIONS :  # action non-greedy或者全为零的情况
            action_name = np.random.choice(ACTIONS)  # 随机选择action里面的动作
        else:  # action greedy贪婪模式
            action_name = state_actions.tolist()
            action_name = action_name.index(max(action_name))  # 返回最大数的索引
            action_name=ACTIONS[action_name]

        return action_name
    def refresh_anan(self,a,b):
        self.anan.place(x=(50+a*100)-40,y=(50+b*100)-40,width=80, height=80)
    def refresh_times(self):
        self.window.showtimes_listbox.delete(0, END)
        self.window.showtimes_listbox.insert(END,'第'+str(self.point_num)+'次尝试')


if __name__ == '__main__':#主函数
    root = tk.Tk()
    root.resizable(False, False)
    basedesk(root)
    root.mainloop()
    os._exit(0)